package gitobject;


import repository.Repository;
import utility.Utility;

import java.io.*;
import java.nio.file.Path;

public abstract class GitObject {
    public enum Type {
        Other("Other", -1),
        Blob("Blob", 0),
        Tree("Tree", 1),
        Commit("Commit", 2);

        private final String name;
        private final int id;

        /**
         * @param name 类型名称
         * @param id   类型编号
         */
        Type(String name, int id) {
            this.name = name;
            this.id = id;
        }

        /**
         * @param id 类型编号
         * @return 返回对应编号的Type类对象
         */
        public static Type valueOf(int id) {
            switch (id) {
                case 0: return  Blob;
                case 1: return  Tree;
                case 2: return  Commit;
                default: return  Other;
            }
        }

        /**
         * @return 返回类型名称
         */
        @Override
        public String toString() {
            return name;
        }
    }

    GitObject() {
    }

    Type type = Type.Other;
    byte[] key = null;
    int info_length = 0;

    /**
     * @return 返回GitObject的哈希值
     */
    public byte[] getKey() {
        return key;
    }

    /**
     * @return 返回GitObject的类型
     */
    public Type getType() {
        return type;
    }

    /**
     * 获取GitObject对象在仓库暂存区中的路径
     *
     * @return
     */
    public Path getObjectPath() {
        return Repository.getInstance().getObjectPath()
                .resolve(Utility.hex2string(key, 0, 1))
                .resolve(Utility.hex2string(key, 1, key.length - 1));
    }

    /**
     * 为子类提供写入额外信息的方法
     *
     * @param out
     * @throws IOException
     */
    public void writeExtendInfo(DataOutputStream out) throws IOException {
    }

    /**
     * 为子类提供读取额外信息的方法
     *
     * @param in
     * @throws IOException
     */
    public void readExtendInfo(DataInputStream in) throws IOException {
    }

    /**
     * 为GitObject类写入文件内容
     *
     * @param out
     * @throws IOException
     */

    public void writeContent(DataOutputStream out) throws IOException {
    }

    /**
     * 恢复仓库工作区目录下的内容
     *
     * @throws IOException
     */

    public void restoreContent() throws IOException {
        restoreContent(Repository.getInstance().getWorkTree());
    }

    /**
     * 恢复GitObject文件到指定路径
     *
     * @param prefix 目标路径
     * @throws IOException
     */
    public void restoreContent(Path prefix) throws IOException {
    }

    /**
     * 向GitObject对象的仓库暂存区文件中写入内容
     *
     * @throws IOException
     */
    public void write() throws IOException {
        // 统计文件头
        ByteArrayOutputStream info_array = new ByteArrayOutputStream();
        DataOutputStream info_out = new DataOutputStream(info_array);
        info_out.writeInt(type.id);
        info_out.write(getKey(), 0, 20);
        writeExtendInfo(info_out);
        info_out.flush();
        info_length = info_array.size();
        //  写入文件
        File obj_file = getObjectPath().toFile();
        obj_file.getParentFile().mkdirs();
        DataOutputStream out = new DataOutputStream(
                new FileOutputStream(obj_file)
        );
        out.writeInt(info_length);
        info_array.writeTo(out);
        info_array.close();
        writeContent(out);
        out.close();
    }

    /**
     * @param bytes
     * @return 将字节数组转化为HashKey值，获取仓库暂存区中对应的GitObject文件，返回相应的实例
     * @throws IOException
     */

    public static GitObject findByKey(byte[] bytes) throws IOException {
        return findByKey(Utility.hex2string(bytes));
    }

    /**
     * @param key
     * @return 通过HashKey值获取仓库暂存区中对应的GitObject文件，返回相应的实例
     * @throws IOException
     */
    public static GitObject findByKey(String key) throws IOException {
        if (key.length() != 40) {
            System.err.println("fatal: Not a valid jit object: " + key);
            System.exit(-1);
        }
        File obj_file = new File(Repository.getInstance().getRepoRoot() + File.separator + "objects"
                + File.separator + key.substring(0, 2)
                + File.separator + key.substring(2)
        );
        if (!obj_file.exists() || !obj_file.isFile()) {
            System.err.println("fatal: Can not found jit object: " + key);
            System.exit(-1);
        }
        DataInputStream in = new DataInputStream(
                new FileInputStream(obj_file)
        );

        int info_length = in.readInt();
        Type type = Type.valueOf(in.readInt());
        GitObject object;
        switch (type) {
            case Blob: object = new Blob();break;
            case Tree: object = new Tree();break;
            case Commit: object = new Commit();break;
            default: throw new RuntimeException(
                    new ClassNotFoundException(String.format("Unexpected Object[%s]", key))
            );
        }
        object.info_length = info_length;
        object.type = type;
        object.key = new byte[20];
        int n = in.read(object.key);
        assert n == 20;
        object.readExtendInfo(in);
        in.close();
        return object;
    }
}
